﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AVLTutorial
{
    public class BinaryTreeNode
    {
        // this node's child nodes
        public BinaryTreeNode leftChildNode;
        public BinaryTreeNode rightChildNode;
        // the actual data stored by this node.
        public int data;

        public int Data { get { return data; } set { data = value; } }

        // keeps track of the depth of this specific node. getMaxDepth updates this.
        public int myDepth = -1;

        // my current position
        double positionX, positionY;

        const double LEVEL_HEIGHT = 90;

        /// <summary>
        /// Constructor which gets the data this node stores.
        /// </summary>
        /// <param name="data"></param>

        public BinaryTreeNode(int data)
        {
            leftChildNode = null;
            rightChildNode = null;
            this.data = data;
        }

        /// <summary>
        /// Temporary function until the F# integration is finished.
        /// </summary>
        /// <param name="data"></param>

        public void addNode(int data)
        {
            BinaryTreeNode nextNode = this;
            BinaryTreeNode lastNode = null;

            while (nextNode != null)
            {
                if (data > nextNode.data)
                {
                    lastNode = nextNode;
                    nextNode = nextNode.rightChildNode;
                }
                else if (data < nextNode.data)
                {
                    lastNode = nextNode;
                    nextNode = nextNode.leftChildNode;
                }
                else
                {
                    return;
                }
            }

            if (data > lastNode.data)
            {
                lastNode.rightChildNode = new BinaryTreeNode(data);
            }
            else
            {
                lastNode.leftChildNode = new BinaryTreeNode(data);
            }
        }

        /// <summary>
        /// Gets the maximum depth of the tree, and also updates the depth of each node. This should be called before rendering, or weird things may happen.
        /// </summary>
        /// <param name="curDepth">The depth of the node this is being called on</param>
        /// <param name="maxDepth">Keeps track of the maximum depth we've found</param>
        /// <returns></returns>

        public int getMaxDepth(int curDepth, int maxDepth)
        {
            myDepth = curDepth;

            if (curDepth > maxDepth) maxDepth = curDepth;

            if (leftChildNode != null) maxDepth = leftChildNode.getMaxDepth(curDepth + 1, maxDepth);
            if (rightChildNode != null) maxDepth = rightChildNode.getMaxDepth(curDepth + 1, maxDepth);

            return maxDepth;
        }

        /// <summary>
        /// creates a new visual for this node to use
        /// </summary>
        /// <param name="mainCanvas">The canvas used by the program.</param>
        /// <param name="treeMaxDepth">The maximum depth of the tree.</param>
        /// <param name="x">The X positon of this node.</param>
        /// <param name="y">The Y positon of this node.</param>
        /// <param name="parentX">The X position of this node's parent, or -1 if it is the root.</param>
        /// <param name="parentY">The Y position of this node's parent, or -1 if it is the root.</param>

        public void visualize(MainPage mainPage, Canvas mainCanvas, int treeMaxDepth, double x, double y, double parentX, double parentY, int parentData)
        {
            positionX = x;
            positionY = y;

           

            // calculate how wide the node bars should be at this level
            //int offset = 20 * (treeMaxDepth - myDepth);
            int offset = (int)(Math.Pow(2, treeMaxDepth - myDepth) * 15);

            // offset applied to the right side
            // used by the root to stop center collisions
            int rightOffset = 0;
            if (rightChildNode != null && rightChildNode.rightChildNode != null) rightOffset = 35;

            // see if there is a corresponding visual node in the list of visual nodes
            VisualNode myVisual = mainPage.findVisualNode(data);
            if (myVisual == null)
            {
                // if the corresponding visual node does NOT exist, create a new one
                mainPage.visualNode.Add(new VisualNode(mainPage, mainCanvas, x, y, data));
            }

            // locate the visual that corresponds to this node
            myVisual = mainPage.findVisualNode(data);

            // instruct the visual to move to our new destination (this might not cause it to move it all if this node has not moved)
            myVisual.setDestination(x, y);
            myVisual.isInUse = true;

            if (myDepth > 0)
            {
                // set our visual node to our parent so we can draw our line correctly
                myVisual.ParentNode = mainPage.findVisualNode(parentData);
                if (myVisual.ParentNode == null)
                    throw (new Exception("A node can't find its parent!"));
            }
            else
            {
                // if we're the root, we need to set it to null so no extraneous line is drawn
                myVisual.ParentNode = null;
            }

            // recursivley move down the tree, displaying each node
            if (leftChildNode != null && rightChildNode != null)
            {
                leftChildNode.visualize(mainPage, mainCanvas, treeMaxDepth, x - offset, y + LEVEL_HEIGHT, x, y, data);
                rightChildNode.visualize(mainPage, mainCanvas, treeMaxDepth, x + offset + rightOffset, y + LEVEL_HEIGHT, x, y, data);
            } else if(leftChildNode != null)
                leftChildNode.visualize(mainPage, mainCanvas, treeMaxDepth, x - offset * 1, y + LEVEL_HEIGHT, x, y, data);
            else if(rightChildNode != null)
                rightChildNode.visualize(mainPage, mainCanvas, treeMaxDepth, x + offset * 1, y + LEVEL_HEIGHT, x, y, data);
 
        }

        public override string ToString()
        {
            return "" + data;
        }
    }
}
